const db = require("../models");
const jwt = require('jsonwebtoken');
const utils = require("../utils");
const config = require("../config");


/**
 * 初始化 SSE 连接
 * @param {Object} req - 请求对象
 * @param {Object} res - 响应对象
 */
exports.init = async (req, res) => {
    // 设置响应头部信息，指定内容类型为'text/event-stream'，这是Server-Sent Events (SSE)的MIME类型
    res.setHeader('Content-Type', 'text/event-stream');
    // 设置响应头部信息，禁用缓存
    res.setHeader('Cache-Control', 'no-cache');
    // 设置响应头部信息，保持连接状态为'keep-alive'，以便服务器可以持续发送事件
    res.setHeader('Connection', 'keep-alive');
    // 发送已设置的头部信息到客户端
    res.flushHeaders();

    // 从请求对象的cookies中尝试获取名为"authorized-token"的token信息，并解析为JSON对象
    // 如果不存在，则使用一个空对象作为默认值
    const token = JSON.parse(req.cookies["authorized-token"] || "{}");

    // 如果token中不存在accessToken属性，说明没有登录，则直接结束响应
    if (!token.accessToken) return res.end();

    // 使用JWT的secret key验证accessToken的有效性，并获取用户信息
    const user = jwt.verify(token.accessToken, config.jwtSecretKey);
    // 尝试从连接池中获取当前用户的SSE客户端信息，在用户登录时会进行创建
    let sseClient = db.sseClient[user.id];
    const uuid = utils.tools.uuid();
    if (!sseClient) {
        db.sseClient[user.id] = {
            userId: user.id,
            info: await utils.ip(req),
            connectSid: user.connectSid,
            sessionId: user.sessionId,
            clientList: [{ uuid, res }]
        }
    } else {
        sseClient.clientList ??= [];
        sseClient.clientList.push({ res, uuid });
    }
    res.write(`data: ${JSON.stringify({ type: 'ok' })}\n\n`)
    // 监听连接关闭事件
    req.on('close', () => {
        // 从当前用户的clientList中过滤出UUID不等于当前连接UUID的客户端对象
        const clientList = db.sseClient[user.id]?.clientList?.filter(item => item.uuid !== uuid) ?? [];

        // 如果过滤后的clientList为空数组，说明当前用户没有其他活动的SSE连接，因此从数据库中删除该用户的SSE客户端信息
        if (!clientList.length) {
            return delete db.sseClient[user.id];
        }

        // 如果过滤后的clientList不为空，则更新数据库中当前用户的SSE客户端信息，只保留活动的连接
        db.sseClient[user.id].clientList = clientList;
    });
};

/**
 * 通过用户ID发送消息
 * @param userId 用户ID
 * @param data 消息内容
 * @param data.type 消息类型
 * @param data.msg 提示内容
 */
exports.sendByUserId = (userId, data) => {
    const clientList = db.sseClient[userId]?.clientList ?? [];
    clientList.forEach(item => item?.res?.write(`data: ${JSON.stringify(data)}\n\n`));
    // 如果消息类型为 强制下线 或 断开连接 ，则删除该用户的SSE客户端信息
    if (['force', 'reconnect'].includes(data.type)) {
        delete db.sseClient[userId]
    }
}
/**
 * 通过 sessionId 或 connectSid 发送消息
 * @param {*} { key:sessionId | connectSid, value:值 }
 * @param {*} { type:消息类型, msg:提示内容,... } 
 */
exports.sendByClient = ({ key, value }, data) => {
    const client_list = (Object.values(db.sseClient)).filter(item => item[key] == value);
    client_list.forEach(item => item?.res?.write(`data: ${JSON.stringify(data)}\n\n`));
}

/**
 * 登录->其他用户退出
 * @param userId 登录用户ID
 * @param sessionId 浏览器指纹
 */
exports.sendByLogin = (userId, sessionId, cb) => {
    const client = db.sseClient[userId];
    if (client) {
        client?.clientList?.forEach(item => {
            item?.res.write(`data: ${JSON.stringify({ type: 'logOut' })}\n\n`)
        })
        db.redis.del(`${client.connectSid}:${client.sessionId}:token`)
        delete db.sseClient[userId];
    }
    const sse = Object.keys(db.sseClient).find(key => db.sseClient[key].sessionId === sessionId) || null;
    if (sse) {
        db.sseClient[sse]?.clientList?.forEach(item => {
            item?.res.write(`data: ${JSON.stringify({ type: 'logOut' })}\n\n`)
        })
        db.redis.del(`${db.sseClient[sse].connectSid}:${db.sseClient[sse].sessionId}:token`)
        delete db.sseClient[sse];
    }
    cb && cb();
}
